using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace Microsoft.Office.Project.EPMAuditing.Library
{
    public class EPMAuditingConfigFile
    {
        private EPMAuditingConfig _epmAuditingConfig;
        private bool _epmAuditingFileRead;
        private string _epmAuditingConfigFileName;
        private Tracing _tracing;
        private bool _skipVersionCheck = false;

        public const string DEFAULT_CONFIG_FILENAME = "EPMAuditing.config";


        #region Initialization

        public EPMAuditingConfigFile() : this(null, DEFAULT_CONFIG_FILENAME, false) { }

        public EPMAuditingConfigFile(string epmAuditingConfigFileName) : this(null, epmAuditingConfigFileName, false) { }

        public EPMAuditingConfigFile(Tracing tracePtr) : this(tracePtr, DEFAULT_CONFIG_FILENAME, false) { }

        public EPMAuditingConfigFile(Tracing tracePtr, string epmAuditingConfigFileName) : this(tracePtr, epmAuditingConfigFileName, false) { }

        public EPMAuditingConfigFile(bool skipVersionCheck) : this(null, DEFAULT_CONFIG_FILENAME, skipVersionCheck) { }

        public EPMAuditingConfigFile(string epmAuditingConfigFileName, bool skipVersionCheck) : this(null, epmAuditingConfigFileName, skipVersionCheck) { }

        public EPMAuditingConfigFile(Tracing tracePtr, bool skipVersionCheck) : this(tracePtr, DEFAULT_CONFIG_FILENAME, skipVersionCheck) { }

        public EPMAuditingConfigFile(Tracing tracePtr, string epmAuditingConfigFileName, bool skipVersionCheck)
        {
            _epmAuditingConfigFileName = epmAuditingConfigFileName;
            _skipVersionCheck = skipVersionCheck;
            
            if (tracePtr != null)
                _tracing = tracePtr;
            
            ReadConfigFromFile();
        }
        #endregion

        #region Properties
        public EPMAuditingConfig AuditingConfiguration
        {
            get
            {
                if (_epmAuditingFileRead)
                    return _epmAuditingConfig;
                else
                    return null;
            }
        }
        #endregion

        #region File I/O Commands

        private void ReadConfigFromFile()
        {
            FileStream fs = null;
            XmlReader reader = null;

            try
            {
                if (XMLSchemaVersionMatches)
                {
                    // Create an instance of the XmlSerializer specifying type and namespace.
                    XmlSerializer serializer = new XmlSerializer(typeof(EPMAuditingConfig));

                    // Verify the sync file exists.
                    FileInfo fi = new FileInfo(_epmAuditingConfigFileName);
                    if (fi.Exists)
                    {
                        // A FileStream is needed to read the XML document.
                        fs = new FileStream(_epmAuditingConfigFileName, FileMode.Open);
                        reader = new XmlTextReader(fs);

                        // Use the Deserialize method to restore the object's state.
                        _epmAuditingConfig = (EPMAuditingConfig)serializer.Deserialize(reader);

                        _epmAuditingFileRead = true;

                        if (_tracing != null)
                            _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, String.Format(CultureInfo.InvariantCulture, "Info: configuration file: {0},  was read sucessfully.", _epmAuditingConfigFileName));
                    }
                    else
                    {
                        _epmAuditingFileRead = false;
                        if (_tracing != null)
                            _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: configuration file: {0},  not found.", _epmAuditingConfigFileName));
                        else
                            throw( new FileNotFoundException("Error: configuration file,  not found", _epmAuditingConfigFileName));

                    }
                }
                else
                {
                    _epmAuditingFileRead = false;
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, "Error: The version of the EPMAuditingConfig assembly didn't match the version of the XML configuration file.");
                    else
                        throw (new FileFormatException("Error: The version of the EPMAuditingConfig assembly didn't match the version of the XML configuration file."));

                }
            }
            catch (ArgumentOutOfRangeException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: FileMode invalid for the configuration file: {0}", _epmAuditingConfigFileName));
                else
                    throw (new FileLoadException("Error: FileMode invalid for the configuration file", _epmAuditingConfigFileName, ex));

            }
            catch (ArgumentNullException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, "Error: The configuration file  was null.");
                else
                    throw (new FileLoadException("Error: The configuration file  was null.", _epmAuditingConfigFileName, ex));

            }
            catch (ArgumentException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file name: {0} was invalid.}", _epmAuditingConfigFileName));
                else
                    throw (new FileLoadException("Error: The configuration file name was invalid.", _epmAuditingConfigFileName, ex));

            }
            catch (SecurityException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The user doesn't hsve the required permissions of open the configuration file: {0}", _epmAuditingConfigFileName));
                else
                    throw (new FileLoadException("Error: The user doesn't hsve the required permissions of open the configuration file.", _epmAuditingConfigFileName, ex));

            }
            catch (UnauthorizedAccessException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: Access to the configuration file: {0} was denied.", _epmAuditingConfigFileName));
                else
                    throw (new FileLoadException("Error: Access to the configuration file was denied.", _epmAuditingConfigFileName, ex));

            }
            catch (PathTooLongException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file: {0} path is too long {1}", _epmAuditingConfigFileName, _epmAuditingConfigFileName.Length));
                else
                    throw (new FileLoadException("Error: The configuration file path is too long", _epmAuditingConfigFileName, ex));

            }
            catch (NotSupportedException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, "Error: There is a colon in the path name");
                else
                    throw (new FileLoadException("Error: There is a colon in the path name", _epmAuditingConfigFileName, ex));

            }
            catch (FileNotFoundException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file: {0},  was not found.", _epmAuditingConfigFileName));
                else
                    throw (ex);

            }
            catch (DirectoryNotFoundException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file directory: {0},  was not found.", _epmAuditingConfigFileName));
                else
                    throw (ex);

            }
            catch (InvalidOperationException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: An InvalidOpertionException occurred reading the configuration file: {0},  Expection: {1}", _epmAuditingConfigFileName, ex.Message));
                else
                    throw (new FileLoadException("Error: An InvalidOpertionException occurred reading the configuration file.", _epmAuditingConfigFileName, ex));

            }
            catch (IOException ex)
            {
                _epmAuditingFileRead = false;
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: An I/O error occurred in the configuration file: {0}", _epmAuditingConfigFileName));
                else
                    throw (new FileLoadException("Error: An I/O error occurred in the configuration file.", _epmAuditingConfigFileName, ex));

            }
            finally
            {
                if (reader != null)
                    reader.Close();

                if (fs != null)
                    fs.Close();

            }
        }



        public bool WriteConfigToFile()
        {
            bool completed = true;
            XmlSerializer serializer = new XmlSerializer(typeof(EPMAuditingConfig));
            FileStream fs = null;
            XmlWriter writer = null;

            try
            {
                // Determine whether the directory exists.
                FileInfo fi = new FileInfo(_epmAuditingConfigFileName);
                if (!Directory.Exists(fi.DirectoryName))
                {
                    // Try to create the directory.
                    Directory.CreateDirectory(fi.DirectoryName);
                }

                // Create an XmlTextWriter using a FileStream.
                fs = new FileStream(_epmAuditingConfigFileName, FileMode.Create);
                writer = new XmlTextWriter(fs, new UTF8Encoding());

                // Serialize using the XmlTextWriter.
                serializer.Serialize(writer, _epmAuditingConfig);

                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, String.Format(CultureInfo.InvariantCulture, "Info: configuration file: {0},  was written to sucessfully.", _epmAuditingConfigFileName));
            }
            catch (ArgumentOutOfRangeException)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: FileMode invalid for the configuration file: {0}", _epmAuditingConfigFileName));

                completed = false;
            }
            catch (ArgumentNullException)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, "Error: The configuration file was null.");

                completed = false;
            }
            catch (ArgumentException)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file name: {0} was invalid.}", _epmAuditingConfigFileName));

                completed = false;
            }
            catch (SecurityException)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The user doesn't hsve the required permissions of open the configuration file: {0}", _epmAuditingConfigFileName));

                completed = false;
            }
            catch (UnauthorizedAccessException)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: Access to the configuration file: {0} was denied.", _epmAuditingConfigFileName));

                completed = false;
            }
            catch (PathTooLongException)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file: {0} path is too long {1}", _epmAuditingConfigFileName, _epmAuditingConfigFileName.Length));

                completed = false;
            }
            catch (NotSupportedException)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, "Error: There is a colon in the path name");

                completed = false;
            }
            catch (FileNotFoundException)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file: {0},  was not found.", _epmAuditingConfigFileName));

                completed = false;
            }
            catch (DirectoryNotFoundException)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file directory: {0},  was not found.", _epmAuditingConfigFileName));

                completed = false;
            }
            catch (InvalidOperationException ex)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: An InvalidOpertionException occurred reading the configuration file: {0},  Expection: {1}", _epmAuditingConfigFileName, ex.Message));

                completed = false;
            }
            catch (IOException)
            {
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: An I/O error occurred in the configuration file: {0}", _epmAuditingConfigFileName));

                completed = false;
            }
            finally
            {
                if (writer != null)
                    writer.Close();

                if (fs != null)
                    fs.Close();
            }

            return completed;

        }

        private bool XMLSchemaVersionMatches
        {
            get
            {
                if (_skipVersionCheck)
                    return true;

                FileStream fs = null;
                XmlReader reader = null;
                string xmlVersion = String.Empty;
                bool readValue = false;

                try
                {
                    fs = new FileStream(_epmAuditingConfigFileName, FileMode.Open);
                    reader = new XmlTextReader(fs);
                    reader.MoveToContent();
                    while (reader.Read())
                    {
                        if (readValue)
                        {
                            xmlVersion = reader.Value;
                            break;
                        }
                        if (reader.LocalName == "SchemaVersion")
                        {
                            readValue = true;
                        }

                    }
                }
                catch (ArgumentOutOfRangeException)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: FileMode invalid for the configuration file: {0}", _epmAuditingConfigFileName));

                    return false;
                }
                catch (ArgumentNullException)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, "Error: The configuration file was null.");

                    return false;
                }
                catch (ArgumentException)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file name: {0} was invalid.}", _epmAuditingConfigFileName));

                    return false;
                }
                catch (SecurityException)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The user doesn't hsve the required permissions of open the configuration file: {0}", _epmAuditingConfigFileName));

                    return false;
                }
                catch (UnauthorizedAccessException)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: Access to the configuration file: {0} was denied.", _epmAuditingConfigFileName));

                    return false;
                }
                catch (PathTooLongException)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file: {0} path is too long {1}", _epmAuditingConfigFileName, _epmAuditingConfigFileName.Length));

                    return false;
                }
                catch (NotSupportedException)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, "Error: There is a colon in the path name");

                    return false;
                }
                catch (FileNotFoundException)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file: {0},  was not found.", _epmAuditingConfigFileName));

                    return false;
                }
                catch (DirectoryNotFoundException)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: The configuration file directory: {0},  was not found.", _epmAuditingConfigFileName));

                    return false;
                }
                catch (InvalidOperationException ex)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: An InvalidOpertionException occurred reading the configuration file: {0},  Expection: {1}", _epmAuditingConfigFileName, ex.Message));

                    return false;
                }
                catch (IOException)
                {
                    if (_tracing != null)
                        _tracing.WriteTrace(_tracing.MainSwitch.TraceError, String.Format(CultureInfo.InvariantCulture, "Error: An I/O error occurred in the configuration file: {0}", _epmAuditingConfigFileName));

                    return false;
                }
                finally
                {
                    if (reader != null)
                        reader.Close();

                    if (fs != null)
                        fs.Close();
                }

                if (xmlVersion == SyncConfigLibraryVersion)
                    return true;
                else
                    return false;
            }
        }


        private string SyncConfigLibraryVersion
        {
            get
            {
                Assembly epmAssembly = Assembly.GetExecutingAssembly();
                System.Reflection.AssemblyName epmAssemblyName = epmAssembly.GetName();
                if (_tracing != null)
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, String.Format(CultureInfo.InvariantCulture, "Info: Assembly Tested: {0}, Version: {1}", epmAssembly.Location, epmAssemblyName.Version.ToString()));
                return epmAssemblyName.Version.ToString();
            }
        }

        #endregion

    }
}
